module axi_bridge(
    input clk,
    input resetn,
    //inst cache read
    input i_rd_req,
    input [2:0] i_rd_type,
    input [31:0] i_rd_addr,
    output i_rd_rdy,
    output i_ret_valid,
    output i_ret_last,
    output [31:0] i_ret_data,
    //inst cache write
    input i_wr_req,
    input [2:0] i_wr_type,
    input [31:0] i_wr_addr,
    input [3:0] i_wr_wstrb,
    input [127:0] i_wr_data,
    output i_wr_rdy,
    //data cache read
    input d_rd_req,
    input [2:0] d_rd_type,
    input [31:0] d_rd_addr,
    output d_rd_rdy,
    output d_ret_valid,
    output d_ret_last,
    output [31:0] d_ret_data,
    //data cache write
    input d_wr_req,
    input [2:0] d_wr_type,
    input [31:0] d_wr_addr,
    input [3:0] d_wr_wstrb,
    input [127:0] d_wr_data,
    output d_wr_rdy, 
    //data sram
//axi interface
    //read acquire
    output [ 3:0] arid, //inst: 0, data: 1
    output [31:0] araddr,
    output [ 7:0] arlen, //set to 0
    output [ 2:0] arsize,
    output [ 1:0] arburst, //set to 2'b01
    output [ 1:0] arlock, //set to 0
    output [ 3:0] arcache, //set to 0
    output [ 2:0] arprot, //set to 0
    output        arvalid,
    input         arready,
    //read
    input  [ 3:0] rid, //inst: 0, data: 1
    input  [31:0] rdata,
    input  [ 1:0] rresp, //ignored
    input         rlast, //ignored
    input         rvalid,
    output         rready,
    //write acquire
    output [ 3:0] awid,
    output [31:0] awaddr,
    output [ 7:0] awlen, //set to 0
    output [ 2:0] awsize,
    output [ 1:0] awburst, //set to 2'b01
    output [ 1:0] awlock, //set to 0
    output [ 3:0] awcache, //set to 0
    output [ 2:0] awprot, //set to 0
    output        awvalid,
    input         awready,
    //write data
    output [ 3:0] wid, //set to 1
    output [31:0] wdata,
    output [ 3:0] wstrb,
    output        wlast, //set to 1
    output        wvalid,
    input         wready,
    //write feedback
    input  [ 3:0] bid, //ignored
    input  [ 1:0] bresp, //ignored
    input         bvalid,
    output        bready
);

wire [2:0] asize_data;
wire [2:0] asize_inst;
wire [7:0] i_rd_len;
wire [7:0] i_wr_len;
wire [7:0] d_rd_len;
wire [7:0] d_wr_len;
assign asize_inst = 3'd2;
assign asize_data = 3'd2;
assign i_rd_len = i_rd_type == 3'b100 ? 8'd3 : 8'd0;
assign i_wr_len = i_wr_type == 3'b100 ? 8'd3 : 8'd0;
//TODO
assign d_rd_len = d_rd_type == 3'b100 ? 8'd3 : 8'd0;
assign d_wr_len = d_wr_type == 3'b100 ? 8'd3 : 8'd0;
/******************** DECALRATION ********************/
    reg [7:0] r_cur_state;
    reg [7:0] r_next_state;
    reg [7:0] w_cur_state;
    reg [7:0] w_next_state;
    //read
    reg [3:0] arid_r;
    reg [31:0] araddr_r;
    reg [2:0] arsize_r;
    reg arvalid_r;
    reg [7:0] arlen_r;
    wire read_after_write;
    reg [2:0] rd_count;
    //write
    reg [3:0] awid_r;
    reg [31:0] awaddr_r;
    reg [2:0] awsize_r;
    reg awvalid_r;
    reg [127:0] wdata_r;
    reg [3:0] wstrb_r;
    reg [7:0] awlen_r;
    wire write_after_read;
    reg [2:0] wr_count;
    //cpu
    wire cpu_inst_read_req;
    wire cpu_inst_write_req;
    wire cpu_data_read_req;
    wire cpu_data_write_req;
/******************** READ STATE MACHINE ********************/
    //define state
    localparam READ_INIT = 8'h01,
               READ_INST_REQ = 8'h02,
               READ_DATA_REQ = 8'h04,
               READ_RECV = 8'h08;
    //upgrade current reading state
    always @(posedge clk) begin
        if(!resetn)
            r_cur_state <= READ_INIT;
        else
            r_cur_state <= r_next_state;
    end
    //generate next state
    always @(*) begin
        case(r_cur_state)
            READ_INIT: begin
                if(cpu_inst_read_req & ~read_after_write)
                    r_next_state = READ_INST_REQ;
                else if(cpu_data_read_req & ~read_after_write)
                    r_next_state = READ_DATA_REQ;
                else
                    r_next_state = READ_INIT;
            end
            READ_INST_REQ, READ_DATA_REQ: begin
                if(arvalid_r & arready)
                    r_next_state = READ_RECV;
                else
                    r_next_state = r_cur_state;
            end
            READ_RECV: begin
                if(rready && rvalid && (rd_count == arlen_r))
                    r_next_state = READ_INIT;
                else
                    r_next_state = READ_RECV;
            end
            default: 
                r_next_state = READ_INIT;
        endcase
    end

    always @(posedge clk) begin
        if(~resetn) begin
            arid_r <= 4'b0;
            arvalid_r <= 1'b0;
            arsize_r <= 3'b0;
            araddr_r <= 32'b0;
            arlen_r <= 8'b0;
        end
        else if(r_cur_state[0] & cpu_inst_read_req & ~read_after_write) begin
            arid_r <= 4'b0;
            arvalid_r <= 1'b1;
            arsize_r <= asize_inst;
            araddr_r <= i_rd_addr;
            arlen_r <= i_rd_len;
        end
        else if(r_cur_state[0] & cpu_data_read_req & ~read_after_write) begin
            arid_r <= 4'b1;
            arvalid_r <= 1'b1;
            arsize_r <= asize_data;
            araddr_r <= d_rd_addr;
            arlen_r <= d_rd_len;
        end
        else if((r_cur_state[1] | r_cur_state[2]) & arready & arvalid) begin       
            arvalid_r <= 1'b0;
        end
        else if(r_cur_state[3] && rready && rvalid && (rd_count == arlen_r)) begin
            arid_r <= 4'b0;
            arsize_r <= 3'b0;
            araddr_r <= 32'b0;
            arlen_r <= 8'b0;
        end
    end
    
    //read burst count
    always @(posedge clk) begin
        if(~resetn)
            rd_count <= 3'b0;
        else if(r_cur_state[1] || r_cur_state[2])
            rd_count <= 3'b0;
        else if(r_cur_state[3] && rready & rvalid)
            rd_count <= rd_count + 3'd1;
    end
/******************** READ COMBINATIONAL LOGIC ********************/

    //read acquire
    assign arid = arid_r;
    assign araddr = araddr_r;
    assign arlen = arlen_r;
    assign arsize = arsize_r;
    assign arburst = 2'b01;
    assign arlock = 2'b0;
    assign arcache = 4'b0;
    assign arprot = 3'b0;
    assign arvalid = arvalid_r;
    //read
    assign rready = r_cur_state == READ_RECV;
    //judge read after write
    assign read_after_write = (cpu_inst_read_req & (awaddr_r[31:4] == i_rd_addr[31:4]) & ~w_cur_state[0])
                            | (cpu_data_read_req & (awaddr_r[31:2] == d_rd_addr[31:2]) & ~w_cur_state[0]);

/******************** WRITE STATE MACHINE ********************/
    //define write state
    localparam WRITE_REQ_INIT = 8'h01,
               WRITE_INST_REQ = 8'h02,
               WRITE_DATA_REQ = 8'h04,
               WRITE_DATA = 8'h08,
               WRITE_WAIT = 8'h10;
    //upgrade current writing state
    always @(posedge clk) begin
        if(!resetn)
            w_cur_state <= WRITE_REQ_INIT;
        else
            w_cur_state <= w_next_state;
    end
    //write state machine
    always @(*) begin
        case(w_cur_state)
            WRITE_REQ_INIT: begin
                if(cpu_inst_write_req & ~write_after_read)
                    w_next_state = WRITE_INST_REQ;
                else if(cpu_data_write_req & ~write_after_read)
                    w_next_state = WRITE_DATA_REQ;
                else
                    w_next_state = WRITE_REQ_INIT;
            end
            WRITE_INST_REQ: begin
                if(awvalid & awready)
                    w_next_state = WRITE_DATA;
                else 
                    w_next_state = WRITE_INST_REQ;
            end
            WRITE_DATA_REQ: begin
                if(awvalid & awready)
                    w_next_state = WRITE_DATA;
                else
                    w_next_state = WRITE_DATA_REQ;
            end
            WRITE_DATA: begin
                if(wvalid && wready && (wr_count == awlen_r))
                    w_next_state = WRITE_WAIT;
                else    
                    w_next_state = WRITE_DATA;
            end
            WRITE_WAIT: begin
                if(bvalid & bready)
                    w_next_state = WRITE_REQ_INIT;
                else
                    w_next_state = WRITE_WAIT;
            end
            default:
                w_next_state = WRITE_REQ_INIT;
        endcase
    end

    always @(posedge clk) begin
        if(~resetn) begin
            awid_r <= 4'b0;
            awaddr_r <= 32'b0;
            awsize_r <= 3'b0;
            awvalid_r <= 1'b0;
            awlen_r <= 8'b0;
            wdata_r <= 128'b0;
            wstrb_r <= 4'b0;
        end
        else if(w_cur_state[0] & cpu_inst_write_req & ~write_after_read) begin
            awid_r <= 4'b0;
            awaddr_r <= i_wr_addr;
            awsize_r <= asize_inst;
            awvalid_r <= 1'b1;
            awlen_r <= i_wr_len;
            wdata_r <= i_wr_data;
            wstrb_r <= i_wr_wstrb;
        end
        else if(w_cur_state[0] & cpu_data_write_req & ~write_after_read) begin
            awid_r <= 4'b1;
            awaddr_r <= d_wr_addr;
            awsize_r <= asize_data;
            awvalid_r <= 1'b1;
            awlen_r <= d_wr_len;
            wdata_r <= d_wr_data;
            wstrb_r <= d_wr_wstrb;
        end
        else if((w_cur_state[2] | w_cur_state[3]) & awvalid & awready) begin
            awvalid_r <= 1'b0;
        end
        else if(w_cur_state[4] && bvalid && bready) begin
            awid_r <= 4'b0;
            awaddr_r <= 32'b0;
            awsize_r <= 3'b0;
            awlen_r <= 8'b0;
            wdata_r <= 128'b0;
            wstrb_r <= 4'b0;
        end
    end

     //write burst count
    always @(posedge clk) begin
        if(~resetn)
            wr_count <= 3'b0;
        else if(w_cur_state[1] || w_cur_state[2])
            wr_count <= 3'b0;
        else if(w_cur_state[3] && wready & wvalid)
            wr_count <= wr_count + 3'd1;
    end
/******************** WRITE COMBINATIONAL LOGIC ********************/
    //write acquire
    assign awid = awid_r;
    assign awaddr = awaddr_r;
    assign awlen = awlen_r;
    assign awsize = awsize_r;
    assign awburst = 2'b01;
    assign awlock = 2'b0;
    assign awcache = 4'b0;
    assign awprot = 3'b0;
    assign awvalid = awvalid_r;
    //write
    assign wid = 4'b1;
    assign wdata = {32{wr_count == 3'd0}} & wdata_r[31:0]
                |  {32{wr_count == 3'd1}} & wdata_r[63:32]
                |  {32{wr_count == 3'd2}} & wdata_r[95:64]
                |  {32{wr_count == 3'd3}} & wdata_r[127:96];
    assign wstrb = wstrb_r;
    assign wlast =  wvalid && (wr_count == awlen_r);
    assign wvalid = w_cur_state == WRITE_DATA;  
    //write feedback
    assign bready = w_cur_state == WRITE_WAIT & ~(rvalid & rready & (rid == awid_r));
    //judge write after read
    assign write_after_read = (cpu_data_write_req & (araddr_r[31:2] == d_wr_addr[31:2]) & ~r_cur_state[0]);

/******************** CPU INTERFACE ********************/
    assign cpu_inst_read_req = i_rd_req;
    assign cpu_inst_write_req = i_wr_req;
    assign cpu_data_read_req = d_rd_req;
    assign cpu_data_write_req = d_wr_req;
    //to cpu
    assign i_ret_last = r_cur_state[3] && (rd_count == arlen_r);
    assign i_rd_rdy = (r_cur_state[1] & arvalid & arready & (arid_r == 4'b0))
                            |  (w_cur_state[1] & awvalid & awready & (awid_r == 4'b0));
    assign i_ret_valid = (r_cur_state[3] & rready & rvalid & (arid_r == 4'b0));
    assign i_ret_data = rdata;

    assign d_ret_last = r_cur_state[3] && (rd_count == arlen_r);
    assign d_rd_rdy = (r_cur_state[2] & arvalid & arready & (arid_r == 4'b1));
    assign d_wr_rdy = (w_cur_state[4] && bvalid && bready);
    assign d_ret_valid = (r_cur_state[3] & rready & rvalid & (arid_r == 4'b1));
    assign d_ret_data = rdata;

endmodule